package roleimpl

import (
	"context"
	"errors"
	"fmt"
	"github.com/casbin/casbin/v2"
	"github.com/grafana/grafana/pkg/cmd/grafana-cli/logger"
	"github.com/grafana/grafana/pkg/log"
	"go-caipu/pkg/services/admin/model"
	"go-caipu/pkg/services/admin/role"
	"go-caipu/pkg/services/dto"
	"go-caipu/pkg/setting"
	"go-caipu/pkg/util"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
	"strconv"
	"time"
)

type store interface {
	GetRolePage(ctx context.Context, cmd role.GetRolePageCommand) ([]model.SysRole, int64, error)
	GetRole(ctx context.Context, cmd role.GetRoleCommand) (model.SysRole, error)
	Insert(post *model.SysRole) error
	Update(post model.SysRole) error
	Remove(cmd role.DelRoleCommand) error
	UpsertPolicy(roleKey string, menuIds []string) (err error)
	GetPermissionByRole(cmd []string) ([]string, error)
	GetDataScope(role int64) ([]string, error)
	UpdateDataScope(cmd role.UpdateDataScopeCommand) (err error)
	UpdateRolePermission(ctx context.Context, cmd role.UpdateRolePermissionCommand) (err error)
	GetUserIdsByRoleId(roleId string) ([]string, error)
	RemoveRoleUser(ctx context.Context, roleId string, userIds []string) error
	AddRoleUser(ctx context.Context, roleId string, userIds []string) error
	BatchCheckEnforce(request [][]interface{}) ([]bool, error)
}

type sqlStore struct {
	db       *gorm.DB
	logger   log.Logger
	setting  *setting.Cfg
	enforcer *casbin.SyncedEnforcer
}

func ProvideStore(db *gorm.DB) sqlStore {
	enforcer := newCasbinEnforcer(db)
	return sqlStore{
		db:       db,
		logger:   log.New("sysuser.store"),
		enforcer: enforcer,
	}
}

func (s sqlStore) GetRolePage(ctx context.Context, cmd role.GetRolePageCommand) (data []model.SysRole, count int64, err error) {
	err = s.db.Model(&model.SysRole{}).Preload("SysMenu").Scopes(
		dto.MakeCondition(cmd.GetNeedSearch()),
		dto.Paginate(cmd.GetPageSize(), cmd.GetPageIndex()),
	).Find(&data).Limit(-1).Offset(-1).Count(&count).Error
	return
}

func (s sqlStore) GetRole(ctx context.Context, cmd role.GetRoleCommand) (model.SysRole, error) {
	var data model.SysRole
	err := s.db.Model(&data).First(&data, cmd.GetId()).Error
	if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
		return data, errors.New(fmt.Sprintf("role:%d record Not found", cmd.GetId()))
	}
	if err != nil {
		s.logger.Info(fmt.Sprintf("role:%d db  error:%s", cmd.GetId(), err.Error()))
		return data, err
	}
	data.MenuIds, err = s.getRoleMenuId(data.RoleId)
	if err != nil {
		logger.Errorf("get menuIds error, %s", err.Error())
		return data, err
	}
	return data, nil
}
func (s sqlStore) getRoleMenuId(roleId int64) ([]string, error) {
	menuIds := make([]string, 0)
	model := model.SysRole{RoleId: roleId}
	if err := s.db.Model(model).Preload("SysMenu").First(&model).Error; err != nil {
		return nil, err
	}
	l := model.SysMenu
	for i := 0; i < len(l); i++ {
		menuIds = append(menuIds, util.Int64ToString(l[i].MenuId))
	}
	return menuIds, nil

}

func (s sqlStore) Insert(cmd *model.SysRole) error {
	var dataMenu []model.SysMenu
	err := s.db.Preload("SysApi").Where("menu_id in ?", cmd.MenuIds).Find(&dataMenu).Error
	if err != nil {
		return err
	}
	cmd.SysMenu = dataMenu

	tx := s.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	err = tx.Create(&cmd).Error
	if err != nil {
		return err
	}
	return nil

}

// UpsertPolicy 更新删除角色的权限
func (s sqlStore) UpsertPolicy(roleKey string, menuIds []string) (err error) {
	err = s.DeletePolicy(roleKey)
	if err != nil {
		return errors.New(fmt.Sprintf(" delete role policy error:%s", err.Error()))
	}
	if len(menuIds) == 0 {
		return nil
	}
	var dataMenu []model.SysMenu
	err = s.db.Preload("SysApi").Where("menu_id in ?", menuIds).Find(&dataMenu).Error
	if err != nil {
		return err
	}
	mp := make(map[string]interface{}, 0)
	var polices = make([][]string, 0)
	for _, menu := range dataMenu {
		for _, api := range menu.SysApi {
			if mp[roleKey+"-"+api.Path+"-"+api.Action] != "" {
				mp[roleKey+"-"+api.Path+"-"+api.Action] = ""
				polices = append(polices, []string{roleKey, api.Path, api.Action})
			}
		}
	}
	err = s.AddPolicy(polices)
	return err
}

func (s sqlStore) Update(cmd model.SysRole) (err error) {
	tx := s.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	var roleData = model.SysRole{}
	var mList = make([]model.SysMenu, 0)
	tx.Preload("SysMenu").First(&roleData, cmd.RoleId)
	tx.Preload("SysApi").Where(" menu_id in ?", cmd.MenuIds).Find(&mList)
	err = tx.Model(&roleData).Association("SysMenu").Delete(roleData.SysMenu)
	if err != nil {
		return err
	}
	roleData.SysMenu = mList
	roleData.RoleName = cmd.RoleName
	roleData.UpdatedAt = time.Now()
	roleData.Status = cmd.Status
	roleData.Sort = cmd.Sort
	roleData.RoleKey = cmd.RoleKey
	roleData.DataScope = cmd.DataScope
	roleData.Remark = cmd.Remark
	db := tx.Session(&gorm.Session{FullSaveAssociations: true}).Save(&roleData)
	if db.Error != nil {
		return err
	}
	if db.RowsAffected == 0 {
		return errors.New(" roleData not found")
	}
	return nil
}

func (s sqlStore) Remove(cmd role.DelRoleCommand) error {
	var err error
	tx := s.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	var role []model.SysRole
	tx.Preload("SysMenu").Preload("SysRole").Find(&role, cmd.Ids)
	db := tx.Select(clause.Associations).Delete(&role)

	if db.Error != nil {
		logger.Errorf("db error:%s", err)
		return err
	}
	if db.RowsAffected == 0 {
		return errors.New("无权更新该数据")
	}
	//
	for _, id := range cmd.Ids {
		s.UpsertPolicy(id, nil)
	}
	return nil
}

// GetPermissionByRole 查询角色对应的权限列表
func (s sqlStore) GetPermissionByRole(cmd []string) ([]string, error) {
	var roleMenu []model.SysRole
	err := s.db.Model(&model.SysRole{}).Where("role_id in ?", cmd).Preload("SysMenu").Find(&roleMenu).Error
	if err != nil {
		return nil, err
	}
	result := make([]string, 0)
	for _, role := range roleMenu {
		for _, menu := range role.SysMenu {
			if !util.Contains(result, menu.Permission) {
				result = append(result, menu.Permission)
			}
		}
	}
	return result, nil
}

// GetDataScope 获取角色数据权限（权限类型：为部门）
func (s sqlStore) GetDataScope(roleId int64) ([]string, error) {
	deptIds := make([]string, 0)
	deptList := make([]deptIdList, 0)
	// 查找部门id，不包括子部门的ID
	if err := s.db.Table("sys_role_dept").
		Select("sys_role_dept.dept_id").
		Joins("LEFT JOIN sys_dept on sys_dept.dept_id=sys_role_dept.dept_id").
		Where("role_id = ? ", roleId).
		Where(" sys_role_dept.dept_id not in(select sys_dept.parent_id from sys_role_dept LEFT JOIN sys_dept on sys_dept.dept_id=sys_role_dept.dept_id where role_id =? )", roleId).
		Find(&deptList).Error; err != nil {
		return nil, err
	}
	for i := 0; i < len(deptList); i++ {
		deptIds = append(deptIds, util.IntToString(deptList[i].DeptId))
	}
	return deptIds, nil
}

type deptIdList struct {
	DeptId int `json:"Id"`
}

// UpdateDataScope 更新角色数据权限（包括部门数据）
func (s sqlStore) UpdateDataScope(cmd role.UpdateDataScopeCommand) (err error) {
	tx := s.db.Begin()
	defer func() {
		if err != nil {
			tx.Rollback()
		} else {
			tx.Commit()
		}
	}()
	var dlist = make([]model.SysDept, 0)

	var curRole = model.SysRole{}
	tx.Preload("SysDept").First(&curRole, cmd.RoleId)
	tx.Where("dept_id in ?", cmd.DeptIds).Find(&dlist)
	err = tx.Model(&curRole).Association("SysDept").Delete(curRole.SysDept)
	if err != nil {
		logger.Errorf("delete SysDept error:%s", err)
		return err
	}
	cmd.Generate(&curRole)
	curRole.SysDept = dlist
	db := tx.Model(&curRole).Session(&gorm.Session{FullSaveAssociations: true}).Debug().Save(&curRole)
	if db.Error != nil {
		logger.Errorf("db error:%s", err)
		return db.Error
	}
	if db.RowsAffected == 0 {
		err = errors.New("无权更新该数据")
		logger.Errorf("db error:%s", err)
		return err
	}
	return
}
func (s sqlStore) UpdateRolePermission(ctx context.Context, cmd role.UpdateRolePermissionCommand) (err error) {
	// 开启事务
	tx := s.db.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			err = fmt.Errorf("panic occurred: %v", r)
		} else if err != nil {
			tx.Rollback()
		} else {
			err = tx.Commit().Error
		}
	}()

	// 1. 验证角色是否存在
	var existingRole model.SysRole
	if err := tx.WithContext(ctx).Where("role_id = ?", cmd.RoleId).First(&existingRole).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return fmt.Errorf("角色不存在")
		}
		return fmt.Errorf("查询角色失败: %v", err)
	}

	// 2. 更新角色的菜单权限
	// 首先删除现有的菜单关联
	if err := tx.WithContext(ctx).Model(&existingRole).Association("SysMenu").Clear(); err != nil {
		return fmt.Errorf("清除现有菜单关联失败: %v", err)
	}

	// 然后添加新的菜单关联
	var menus []model.SysMenu
	if err := tx.WithContext(ctx).Where("menu_id IN ?", cmd.MenuIds).Find(&menus).Error; err != nil {
		return fmt.Errorf("查询菜单失败: %v", err)
	}

	if err := tx.WithContext(ctx).Model(&existingRole).Association("SysMenu").Append(menus); err != nil {
		return fmt.Errorf("更新菜单关联失败: %v", err)
	}
	return s.UpsertPolicy(util.Int64ToString(cmd.RoleId), cmd.MenuIds)
}
func (s sqlStore) GetUserIdsByRoleId(roleId string) ([]string, error) {
	var userIds []string
	if err := s.db.Model(&model.SysUserRole{}).Where("role_id = ?", roleId).Pluck("user_id", &userIds).Error; err != nil {
		return nil, err
	}
	return userIds, nil
}
func (s sqlStore) RemoveRoleUser(ctx context.Context, roleId string, userIds []string) error {
	result := s.db.WithContext(ctx).Where("role_id = ? and user_id IN ?", roleId, userIds).Delete(&model.SysUserRole{})
	return result.Error
}
func (s sqlStore) AddRoleUser(ctx context.Context, roleId string, userIds []string) error {
	// 转换 roleId 为 int64
	rid, err := strconv.ParseInt(roleId, 10, 64)
	if err != nil {
		return fmt.Errorf("invalid roleId format: %v", err)
	}
	var existsUserIds []string
	if err := s.db.Model(&model.SysUserRole{}).Where("role_id =? ", rid).Pluck("user_id", &existsUserIds).Error; err != nil {
		return fmt.Errorf("failed to check existing userIds: %v", err)
	}
	// 转换 userIds 为 int64 切片
	uids := make([]int64, 0, len(userIds))
	for _, uidStr := range userIds {
		uid, err := strconv.ParseInt(uidStr, 10, 64)
		if err != nil {
			return fmt.Errorf("invalid userId format '%s': %v", uidStr, err)
		}
		//检查用户存在则跳过
		if util.Contains(existsUserIds, uidStr) {
			continue
		}
		uids = append(uids, uid)
	}

	// 没有有效用户 ID 时直接返回
	if len(uids) == 0 {
		return nil
	}

	// 构造需要插入的用户角色关系
	var newUsers []model.SysUserRole
	for _, uid := range uids {
		newUsers = append(newUsers, model.SysUserRole{
			UserId: uid,
			RoleId: rid,
		})
	}

	// 使用 GORM 的 OnConflict 忽略重复记录
	if err := s.db.WithContext(ctx).
		Clauses(clause.OnConflict{DoNothing: true}).
		Create(&newUsers).Error; err != nil {
		return fmt.Errorf("failed to add role users: %v", err)
	}

	return nil
}
